using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.ReportingServices.ReportRendering;
using System.Text.RegularExpressions;
using System.Collections;

namespace CustomExcelRenderCS
{
    internal class TextBoxFormulaHandler:FormulaHandler
    {
        private const int asciiA = 65;
        /// <summary>
        /// sum values from top to bottom
        /// </summary>
        private const string cSum_no_formula = "[no_formula]";
        private const string cSum_up = "f..sum_up";
        private const string cSum_down_subreport = "f..sum_down_subreport";
        private const string cSum_matrix_up = "f..sum_matrix_up";
        private const string cSum_matrix_up_other = "f..other_sum_matrix_up";
        private const string cReplace = "[r]";

        private const string cOperatorPattern = @"[,\-+*/]";

        private const string cCurrentObject = "this";
        private const string upPos = "up",
            downPos = "down",
            leftPos = "left",
            rightPos = "right";
  
        private static double mStartRow;
        private static double mEndRow;
        private static double mCurrentCol;

        internal static int tableOffset;
        /// <summary>
        /// object used to fetch current matrix cache info
        /// </summary>
        private MatrixCache mCache;

        /// <summary>
        /// if we are referencing a differenet matrix, set the name here
        /// </summary>
        private string otherMatrixName;
        /// <summary>
        /// number of columns in a matrix group relative to current matrix group
        /// </summary>
        private double?otherMatrixOffset=null;
        /// <summary>
        /// hashtable used to save parameters used by other matrix
        /// </summary>
        private Hashtable otherMatrixParams;
        /// <summary>
        /// current matrix being processed
        /// </summary>
        private MatrixCache curMatrixCache;
        /// <summary>
        /// previous cahce used by other matrix that is not currently being processed
        /// </summary>
        private MatrixCache otherMatrixCache; 

       // private double fixedStartRow;

        private enum formulaItemCheck{
            targetObject,
            relativePosition
        }
        
        private string textBoxFormula
        {
            get
            {
                return ((TextBox)targetReportItem).Formula.ToLower();
            }
        }

        private string textBoxValue
        {
            get
            {
                return ((TextBox)targetReportItem).Value;
            }
         }

         /// <summary>
         /// column equivalent to the ralative position from start column
         /// </summary>
        internal static double currentCol
        {
            get
            {
                return mCurrentCol;
            }
            set
            {
                mCurrentCol = value;
            }
        }

        /// <summary>
        /// row equivalent to the ralative position from start row
        /// </summary>
        private double currentRow
        {
            get
            {
                return startRow + TableRenderer.curCellRow;
            }
            set
            {
            }
        }

        /// <summary>
        /// row equivalent to the ralative position from start row
        /// </summary>
        internal static double startRow
        {
            get
            {
                return mStartRow;
            }
            set
            {
                mStartRow = value;
            }
        }

        /// <summary>
        /// row equivalent to the ralative position from start row
        /// </summary>
        internal static double endRow
        {
            get
            {
                return mEndRow;
            }
            set
            {
                mEndRow = value;
            }
        }

        /// <summary>
        /// get column name in character value
        /// </summary>
        private string currentColName
        {
            get
            { 
                return getExcelColumnName(currentCol);
            }
            set
            {
            }
        }

        /// <summary>
        /// current report item data element name
        /// </summary>
        private string currentItemDataName
        {
            get
            {
                return ((TextBox)targetReportItem).DataElementName;
            }
         }

         /// <summary>
         /// current item position in data column list
         /// </summary>
        private int currentItemPosition
        {
            get
            {
                return TableRenderer.hDataElements[currentItemDataName]!=null?(int)TableRenderer.hDataElements[currentItemDataName]:-1;
            }
            set
            {
            }
        }

        /// <summary>
        /// Get and Set other matrix name property
        /// </summary>
        internal string OtherMatrixName
        {
            get
            {
                if (otherMatrixName == null)
                {                   
                    otherMatrixName = (string)OtherMatrixParams["name"];                    
                }
                return otherMatrixName;
            }
            set
            {
                otherMatrixName = value;
            }
        }

        /// <summary>
        /// property used for other matrix, see otherMatrixOffset field
        /// </summary>
        internal double OtherMatrixOffset
        {
            get
            {
                if (otherMatrixOffset == null)
                {                    
                   otherMatrixOffset = (double)Convert.ToDouble(OtherMatrixParams["step"]);                    
                }
                return (double)otherMatrixOffset;
            }
            set
            {
                otherMatrixOffset = value;
            }
        }

        /// <summary>
        /// property used to save parameters used by other matrix object
        /// </summary>
        internal Hashtable OtherMatrixParams
        {
            get
            {
                if (otherMatrixParams == null)
                {
                    otherMatrixParams = new Hashtable();
                    string textFormula = textBoxFormula.Substring(textBoxFormula.IndexOf(cSum_matrix_up_other) + cSum_matrix_up_other.Length + 1);
                    string[] formulaArray = textFormula.Split(' ');
                    foreach (string faItems in formulaArray)
                    {
                        string[] faItemArray = faItems.Split('=');
                        {
                            otherMatrixParams.Add(faItemArray[0], faItemArray[1]);
                        }
                    }
                }
                return otherMatrixParams;
            }
            set
            {
                otherMatrixParams = value;
            }
        }

        /// <summary>
        /// get and set current matrix cache
        /// </summary>
        internal MatrixCache CurMatrixCache
        {
            get
            {
                if (curMatrixCache == null)
                {
                    string curItemParent = Renderer.reportRenderCache.currentObjParent;
                    curMatrixCache = (MatrixCache)Renderer.reportItemHashTable[curItemParent];
                }
                return curMatrixCache;
            }
            set
            {
                curMatrixCache = value;
            }
        }

        /// <summary>
        /// property used to get and set otherMatrixCache
        /// </summary>
        internal MatrixCache OtherMatrixCache
        {
            get
            {
                if (otherMatrixCache == null)
                {
                    otherMatrixCache = (MatrixCache)Renderer.reportItemHashTable[OtherMatrixName];
                }
                return otherMatrixCache;
            }
            set
            {
                otherMatrixCache = value;
            }
        }

         /// <summary>
         /// get the Excel formula for a text box
         /// </summary>
        internal override string getFormula()
        {
            string f = textBoxFormula;
            string v = textBoxValue;

            //startRow = TableRenderer.startRow;
            //endRow =startRow + TableRenderer.totRows - 1;

            if (!f.ToLower().Contains(cSum_no_formula) && f.ToLower().Contains("sum"))
            {
                formula = evaluateSum();
            }
            else if (Regex.IsMatch(f, cOperatorPattern))
            {
                formula = evaluateOperator();
            }
            else
                formula = v;

            return formula;
        }

        /// <summary>
        /// processing sum expression to sum formula in Excel
        /// </summary>
        private string evaluateSum()
        {
            string sumFormula=textBoxValue;
            double curStartRow = 0;

            if (textBoxFormula.Contains(cReplace))
            {
                return evaluateReplace();
            }
            else if (textBoxFormula.Contains(cSum_matrix_up))
            {
                if (CurMatrixCache.isReadyForSumUp)
                {
                    startRow = CurMatrixCache.matrixStartRow;
                    endRow = CurMatrixCache.matrixEndRow;
                    currentCol = CurMatrixCache.matrixCurCol;
                    sumFormula = "=sum(" + currentColName + startRow + ":" + currentColName + endRow + ")";
                }
            }
            else if (textBoxFormula.Contains(cSum_matrix_up_other))
            {                
                startRow = OtherMatrixCache.matrixStartRow;
                endRow = OtherMatrixCache.matrixEndRow;

                currentCol = CurMatrixCache.matrixStartCol + OtherMatrixOffset * (CurMatrixCache.mCounter++);
                
                sumFormula = "=sum(" + currentColName + startRow + ":" + getExcelColumnName(currentCol+OtherMatrixOffset-1) + endRow + ")";                
            }
            else if (textBoxFormula.Contains(cSum_up) || textBoxFormula.Contains(cSum_down_subreport) || Regex.IsMatch(textBoxFormula, "=[ ]*sum", RegexOptions.IgnoreCase))        // textBoxFormula.ToLower().Contains("=sum"))
            {
                string cCol = currentColName;       //new string((char)(asciiA + currentCol), 1);
                curStartRow = startRow;
                if (textBoxFormula.Contains(cSum_down_subreport))
                {
                    curStartRow += TableRenderer.subreportStartRow;
                    endRow = curStartRow + ListRenderer.subreportTableNoRows - 1;
                }
                sumFormula = "=sum(" + cCol + curStartRow + ":" + cCol + endRow + ")";
            }

            return sumFormula;
        }

        /// <summary>
        /// process operators like + - * / in string
        /// </summary>
        private string evaluateOperator()
        {
            string opFormula = textBoxValue;

            if (textBoxFormula.ToLower().Contains(cReplace))
            {
                return evaluateReplace();
            }

            string txtFormula = textBoxFormula.ToLower().Replace("=","").Replace(" ","").Replace("fields!","").Replace(".value","");
            string[] m = Regex.Split(txtFormula, cOperatorPattern);

            string returnFormula = "=" + txtFormula;
            foreach (string f in m)
            {
                int cellRelativePosition = TableRenderer.hDataElements[f]!=null?(int)TableRenderer.hDataElements[f]:-1;
                int curItemPos = currentItemPosition;
                string mColname = cellRelativePosition>=0?getExcelColumnName(cellRelativePosition):"";
                returnFormula = mColname != "" ? returnFormula.Replace(f, mColname + currentRow) : textBoxValue;
            }
            return returnFormula;
        }

        /// <summary>
        /// get matched Excel column name
        /// </summary>
        private string getExcelColumnName(double iCol)
        {
            if (iCol > 25)
            {
                int firstCharNumber = (int)iCol / 26 -1;
                string firstChar = new string((char)(asciiA + firstCharNumber), 1);
                int secondCharNumber =(int) iCol % 26;
                string secondChar = new string((char)(asciiA + secondCharNumber), 1);
                return firstChar + secondChar;
            }
            return new string((char)(asciiA + iCol), 1);
        }

        /// <summary>
        /// process expression and get replaced comment value
        /// </summary>
        private string evaluateReplace()
        {
            string sumFormula = textBoxFormula;
            string convertedFormula = sumFormula.Substring(sumFormula.IndexOf(cReplace) + cReplace.Length);
            string[] formulaItems  = Regex.Split(convertedFormula,cOperatorPattern);

            foreach (string fItem in formulaItems)
            {
                if (fItem.Trim() == "")
                    continue;

                Match m = Regex.Match(fItem, @"(this|left|right).*\..*\d+");
                if (!m.Success)
                    continue;

                string mItem = m.ToString();
                string[] items = mItem.Split('.');

                string colName = currentColName;
                double rowNumber = currentRow;

                string curExpressionObj = items[(int)formulaItemCheck.targetObject].Trim();
                if (curExpressionObj != cCurrentObject)
                {
                    if (curExpressionObj.Contains(leftPos))
                    {
                        double pos = Convert.ToDouble(curExpressionObj.Replace(leftPos,""));
                        double col = currentCol - pos;
                        colName = getExcelColumnName(Convert.ToInt16(col));
                    }
                }

                string rPosition = items[(int)formulaItemCheck.relativePosition];
                double offset = tableOffset;  // TableRenderer.subReportOffset;
                //offset = offset > 0 ? offset : 0;

                if (rPosition.Contains(upPos))
                {
                    double pos = Convert.ToDouble(rPosition.Replace(upPos, ""));

                    if (pos == 0)
                    {
                        //rowNumber = startRow + (offset>0?offset+1:offset) - 1;
                        rowNumber = startRow + (offset > 0 ? offset + 1 : offset);
                    }
                    else
                    {
                        //rowNumber = currentRow + offset - pos - 1;
                        rowNumber = currentRow + offset - pos;
                    }
                    //convertedFormula = convertedFormula.Replace(mItem, currentColName + pos);
                }
                else if (rPosition.Contains(downPos))
                {
                    double pos = Convert.ToDouble(rPosition.Replace(downPos, ""));
                    rowNumber = currentRow + pos - 1;
                    //convertedFormula = convertedFormula.Replace(mItem, currentColName + pos);
                }
                else if (rPosition.Contains(leftPos))
                {
                    double pos = Convert.ToDouble(rPosition.Replace(leftPos, ""));
                    pos = currentCol - pos;
                    colName = getExcelColumnName(Convert.ToInt16(pos));
                    rowNumber = currentRow + offset;
                    //rowNumber = (currentRow - 1);
                    //convertedFormula = convertedFormula.Replace(mItem, getExcelColumnName(Convert.ToInt16(pos)) + (currentRow - 1));
                }
                else if (rPosition.Contains(rightPos))
                {
                    double pos = Convert.ToDouble(rPosition.Replace(rightPos, ""));
                    pos = currentCol + pos;
                    colName = getExcelColumnName(Convert.ToInt16(pos));
                    rowNumber = currentRow + offset;
                }
                convertedFormula = convertedFormula.Replace(mItem, colName + rowNumber);
            }

            return "=" + (convertedFormula.Contains("sum") ? convertedFormula.Replace("+", ":") : convertedFormula);
        }
    }

    
}
